package cn.csg.ams.collect2.poptopics.redisPool;

import cn.csg.ams.collect2.poptopics.dao.MkPrPfOutageBlackListDao;
import cn.csg.ams.collect2.poptopics.entity.vo.MkPrPfOutageRecoveryMonVO;
import cn.csg.ams.collect2.poptopics.infrastructure.constant.UserCategoryConstant;
import cn.csg.ams.collect2.poptopics.infrastructure.utils.DateUtil;
import cn.csg.ams.collect2.poptopics.infrastructure.utils.JsonUtil;
import lombok.Data;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;

import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CompletableFuture;

/**
 * 停电池
 * 用与记录所有研判结果为停电的对象信息
 * 以对象的运行电能表标识为key
 */
@Component
public class OutagePool {

    @Autowired
    private StringRedisTemplate redisTemplate;

    @Autowired
    private MkPrPfOutageBlackListDao mkPrPfOutageBlackListDao;

    /**
     * 停电池中key的前缀-台区
     */
    private static final String TA_OUTAGE_KEY = "ta_outage_";

    /**
     * 停电池中key的前缀-线路
     */
    private static final String LINE_OUTAGE_KEY = "line_outage_";

    /**
     * 记录停电数量key的前缀
     */
    public static final String OUTAGE_COUNT_KEY = "outage_count_";

    /**
     * 台区复电时间key
     */
    public static final String TA_OUTAGE_END_TIME = "ta_outage_end_time_";

    /**
     * 存储正在停电的电能表标识
     */
    public static final String OUTAGE_RUN_METER_ID_SET = "outage_run_meter_id_set";


    /**
     * 获取停电池的key
     */
    private String getKey(String taNo, String areaCode) {
        return TA_OUTAGE_KEY + areaCode + "_" + taNo;
    }


    // ============= 以电能表标识为key，存储电能表的停电状态 ==============

    public Boolean isOutage(Object runMeterId, Object areaCode) {
        String o = areaCode + "-" + runMeterId;
        return redisTemplate.opsForSet().isMember(OUTAGE_RUN_METER_ID_SET, o);
    }

    public void addOutageSet(String... values) {
        redisTemplate.opsForSet().add(OUTAGE_RUN_METER_ID_SET, values);
    }

    public void removeOutageSet(Object... values) {
        redisTemplate.opsForSet().remove(OUTAGE_RUN_METER_ID_SET, values);
    }

    // =============================================================


    /**
     * 获取停电池的value
     */
    private String getValue(String alarmTime, MkPrPfOutageRecoveryMonVO vo) {
        // 告警时间
        // String alarmTime = TupleRecordDataUtils.getStringField(data, 23);

        OutageInfo outageInfo = new OutageInfo();
        // 终端编号
        outageInfo.setRunTermId(vo.getRunTermNane());
        // 终端类型
        outageInfo.setTermType(vo.getAlarmType());
        if (UserCategoryConstant.BRANCH.equals(vo.getObjType())) {
            outageInfo.setTermType("045");
        }
        outageInfo.setRunMeterId(vo.getRunMeterId());
        outageInfo.setAlarmSourCode("2");
        outageInfo.setAlarmTime(alarmTime);
        // 停电事件id
        outageInfo.setEventId(vo.getOutageEventId());
        outageInfo.setObjectType(vo.getObjType());
        return JsonUtil.toJson(outageInfo);
    }


    /**
     * 获取停电记录
     */
    public OutageInfo getOutageInfo(String taNo, Object runMeterId, String areaCode) {
        String key = getKey(taNo, areaCode);
        Object value = redisTemplate.opsForHash().get(key, runMeterId);
        if (value == null) {
            return null;
        }
        return JsonUtil.toObject(value.toString(), OutageInfo.class);
    }

    /**
     * 获取台区下所有停电信息
     */
    public List<OutageInfo> getOutageInfoByTaNo(String taNo, String areaCode) {
        String key = getKey(taNo, areaCode);
        Map<Object, Object> entries = redisTemplate.opsForHash().entries(key);

        // 解析对象
        List<OutageInfo> outageInfoList = new ArrayList<>();
        for (Map.Entry<Object, Object> entry : entries.entrySet()) {
            Object value = entry.getValue();
            if (value == null) {
                continue;
            }
            OutageInfo alarmInfo = JsonUtil.toObject(value.toString(), OutageInfo.class);
            outageInfoList.add(alarmInfo);
        }
        return outageInfoList;
    }


    /**
     * 删除单条停电记录
     */
    public void deleteOutageInfo(String taNo, String runMeterId, String areaCode) {
        // 删除停电池中的数据
        String key = getKey(taNo, areaCode);
        redisTemplate.opsForHash().delete(key, runMeterId);

        // 删除set池中的数据
        removeOutageSet(areaCode + "-" + runMeterId);
    }


    /**
     * 删除台区下所有停电记录
     */
    public void deleteTaOutageInfo(String taNo, String areaCode) {
        // 先查询台区下所有停电电表
        List<OutageInfo> taList = getOutageInfoByTaNo(taNo, areaCode);

        // 删除台区的所有停电信息
        String key = getKey(taNo, areaCode);
        redisTemplate.delete(key);

        // 删除set池中的停电电表
        Object[] runMeterIds = taList.stream().map(o -> areaCode + "-" + o.getRunMeterId()).toArray(Object[]::new);
        removeOutageSet(runMeterIds);
    }


    /**
     * 将停电信息添加到停电池中
     *
     * @param list      所有停电的对象
     * @param alarmTime 引发停电的告警信息
     */
    public void putOutageInfoToRedis(List<MkPrPfOutageRecoveryMonVO> list, String alarmTime, String taNo, String areaCode) {
        String key = getKey(taNo, areaCode);
        for (MkPrPfOutageRecoveryMonVO vo : list) {
            String hashKey = vo.getRunMeterId();
            String value = getValue(alarmTime, vo);
            Boolean isAbsent = redisTemplate.opsForHash().putIfAbsent(key, hashKey, value);
            if (isAbsent) {
                // 插入成功，记录停电一次
                incrementOutageCount(vo.getRunMeterId(), vo.getRunTermNane());
            }

            // 加入电表停电池，用于快速过滤电流电压、表码等数据
            String[] runMeterIds = list.stream().map(o -> o.getAreaCode() + "-" + o.getRunMeterId()).toArray(String[]::new);
            addOutageSet(runMeterIds);
        }
    }


    /**
     * 记录电表停电次数, 当达到三次时，记录到黑名单中
     */
    public void incrementOutageCount(String runMeterId, String runTermId) {
        String key = OUTAGE_COUNT_KEY + DateUtil.format(new Date(), "yyyy-MM-dd");
        Long count = redisTemplate.opsForHash().increment(key, runMeterId, 1);

        if (count > 3) {
            // 一个电能表一天停电超过3次，记录该电表为黑名单
            CompletableFuture.runAsync(() -> {
                mkPrPfOutageBlackListDao.insertBlackList(runMeterId, runTermId);
                // 同时插入智能运维供电
                mkPrPfOutageBlackListDao.insertWorkOrder(runMeterId, new SimpleDateFormat("yyyyMMdd").format(new Date()));
            });
        }
    }


    // ================= 线路 =========================

    /**
     * 以线路编号为key，存储台区的停电情况
     *
     * @param lineNo    线路编号
     * @param areaCode  地区编码
     * @param taNo      停电台区编号
     * @param alarmTime 停电时间
     */
    public void saveTaOutageToLine(String lineNo, String areaCode, String taNo, String alarmTime) {
        String key = LINE_OUTAGE_KEY + areaCode + "_" + lineNo;
        redisTemplate.opsForHash().put(key, taNo, alarmTime);
    }


    /**
     * 获取线路下所有停电台区
     *
     * @param lineNo   线路编号
     * @param areaCode 地区编码
     */
    public Map<Object, Object> getTaOutageByLine(String lineNo, String areaCode) {
        String key = LINE_OUTAGE_KEY + areaCode + "_" + lineNo;
        return redisTemplate.opsForHash().entries(key);
    }


    /**
     * 查询是否存在线路停电
     *
     * @param lineNo   线路编号
     * @param areaCode 地区编码
     */
    public String getLineOutage(String lineNo, String areaCode) {
        String key = LINE_OUTAGE_KEY + areaCode + "_" + lineNo;
        Object outageStartTime = redisTemplate.opsForHash().get(key, lineNo);
        return outageStartTime == null ? null : outageStartTime.toString();
    }


    /**
     * 删除线路中的停电记录
     *
     * @param lineNo   线路编号
     * @param taNo     台区编号
     * @param areaCode 地区编码
     */
    public void deleteLineOutage(String lineNo, String taNo, String areaCode) {
        String key = LINE_OUTAGE_KEY + areaCode + "_" + lineNo;
        redisTemplate.opsForHash().delete(key, taNo);
    }

    // ======================  台区复电时间 =====================

    /**
     * 记录台区的停电结束时间
     *
     * @param taNo          台区编号
     * @param areaCode      地区编码
     * @param outageEndTime 台区停电结束时间
     */
    public void setTaOutageEndTime(String taNo, String areaCode, String outageEndTime) {
        String key = TA_OUTAGE_END_TIME + areaCode + "_" + taNo;
        redisTemplate.opsForValue().set(key, outageEndTime);
    }


    /**
     * 获取台区的停电结束时间
     *
     * @param taNo     台区编号
     * @param areaCode 地区编码
     */
    public String getTaOutageEndTime(String taNo, String areaCode) {
        String key = TA_OUTAGE_END_TIME + areaCode + "_" + taNo;
        return redisTemplate.opsForValue().get(key);
    }


    /**
     * 停电池中的数据实体
     */
    @Data
    public static class OutageInfo {

        /**
         * 运行终端标识
         */
        public String runTermId;

        /**
         * 运行终端类型( 03.负荷终端、 04.配变终端、 05.集中器、 054.分支)
         */
        public String termType;

        /**
         * 运行电表标识
         */
        public String runMeterId;

        /**
         * 告警来源 {1终端、2电表}
         */
        public String alarmSourCode;

        /**
         * 停电开始时间
         */
        private String alarmTime;

        /**
         * 停电事件id
         */
        private String eventId;

        private String objectType;
    }

}
